# -*- coding: utf-8 -*-

from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core import urlresolvers
from django.contrib.auth.models import User
from game.models import Ant
from game.models import Base, BattleReport, Uphold, MovementResources
from django.conf import settings
from django.db.models import Q
import math

class Attack(models.Model):

    CH_STATUS = (
        ('P', u'Processing'), # En viaje
        ('F', u'Finished'), # En lucha
        ('B', u'Backed'), # Tropas de vuelta
        ('C', u'Conquered'), # Finalizado tras conquistar
        ('S', u'Stamped'), # Finalizado tras estampar
        ('FB', u'Finished Backed'), # Finalizado tras volver
        ('N', u'Null'), # Si conquistan o es destruido el hormiguero de origen
    )

    status = models.CharField(verbose_name=_(u'Status attack'), max_length=7,
            blank=False, null=False, choices=CH_STATUS)

    base_start = models.ForeignKey('game.Base', verbose_name=_(u"Base Start"),
            blank=False, null=False, related_name='attacks_start')

    base_finish = models.ForeignKey('game.Base', verbose_name=_(u"Base Finish"),
            blank=False, null=False, related_name='attacks_finish')

    seconds = models.PositiveIntegerField(verbose_name=_(u'Seconds'),
            blank=True, null=True)

    stolen_resources = models.PositiveIntegerField(
            verbose_name=_(u'Stolen Resources'), blank=True, null=True)

    date_created = models.DateTimeField(null=False, auto_now_add=True)

    date_updated = models.DateTimeField(null=False, auto_now=True)


    class Meta:
        app_label = 'game'
        verbose_name = _(u'Attack')
        verbose_name_plural = _(u'Attacks')

    def __unicode__(self):
        return unicode(self.id) + u' -- ' + unicode(self.get_status_display())


    # Impacto en el ataque
    def resolve_attack(self):
        self.status = 'F'
        # self.save() para tener constancia de ataques que están resolviéndose
        ants = Ant.objects.filter(in_attack = self)

        # Se actualiza el hormiguero porque ha podido haber cambios en el viaje
        self.base_finish = Base.objects.get(id = self.base_finish.id)
        self.base_finish.save()

        if not self.base_finish.player: # Free Anthill
            print u'Hormiguero vacío'

            ants_attack = self.comma_separated_ants(ants)
            ants_defend = '0,0,0,0,0,0'
            name_start = self.base_start.player.name
            name_finish = "" # Free Anthill

            queen = ants.filter(type_ant='Q')

            if queen.exists(): # Attack Conquest
                self.take_anthill(ants, queen)
                info = _(u'El hormiguero ' + 
                            self.base_finish.get_coordinates_string()
                            + u' ha sido conquistado.')
                self.status = 'C'
                self.save()

            else: # Attack back
                info = _(u'El hormiguero ' +
                        self.base_finish.get_coordinates_string()
                        + u' está desocupado. No hay hormiga princesa. '
                        + 'No hay conquista.')
                self.status = 'B'
                self.save()

            # Generando Reporte
            report = BattleReport(attack=self, ants_attack_start=ants_attack,
                        ants_attack_finish=ants_attack,
                        ants_defend_start=ants_defend,
                        ants_defend_finish=ants_defend, name_start=name_start,
                        name_finish=name_finish, info=info)
            report.save()

        else: # Ocupated Anthill
            self.resolve_battle(ants)

        self.save()


    def save(self, ants=None, *args, **kwargs):

        if not self.pk: # Attack initialized
            super(Attack, self).save(*args, **kwargs)

            ants_base = self.base_start.ants_base.filter(
                    player=self.base_start.player, position = 'C')

            self.ants_attach_to_attack(ants_base.filter(type_ant = 'W'), ants['ant_0'])
            self.ants_attach_to_attack(ants_base.filter(type_ant = 'OO'), ants['ant_1'])
            self.ants_attach_to_attack(ants_base.filter(type_ant = 'OD'), ants['ant_2'])
            self.ants_attach_to_attack(ants_base.filter(type_ant = 'FO'), ants['ant_3'])
            self.ants_attach_to_attack(ants_base.filter(type_ant = 'FD'), ants['ant_4'])
            self.ants_attach_to_attack(ants_base.filter(type_ant = 'Q'), ants['ant_5'])

            self.seconds = self.calculate_seconds()

        super(Attack, self).save(*args, **kwargs)


    def resolve_back(self, ants):
        if self.stolen_resources:
            self.base_start.resources += self.stolen_resources
            self.base_start.save()

        ants.update(in_attack=None, position='C')
        self.status = 'FB'
        self.save()


    # private
    def resolve_battle(self, ants):
        invisible = False
        ants_finish = self.base_finish.ants_base.filter(position='C')

        ants_attack_start = self.comma_separated_ants(ants)
        ants_defend_start = self.comma_separated_ants(ants_finish)

        total_force_start = self.get_power_attack(ants)
        total_force_finish = self.get_power_defense(ants_finish)
        bonus_start, bonus_finish = self.calculate_bonus(total_force_start, total_force_finish)
        total_force_start += bonus_start
        total_force_finish += bonus_finish
    
        name_start = self.base_start.player.name
        name_finish = self.base_finish.player.name

        if total_force_start > total_force_finish: # attacker win
            print "Gana atacante"
            player_finish = self.base_finish.player
            game = player_finish.game
            self.base_start.player.kill_points += ants_finish.count()
            self.base_start.player.save()
            ants_finish.delete()

            p = 100 * pow(total_force_finish / total_force_start, 1.5)

            num_ants_dead = self.remove_ants(ants, p)
            self.base_finish.player.kill_points += num_ants_dead
            self.base_finish.player.save()

            ants = Ant.objects.filter(in_attack = self) # Intentar no tener que hacer ésta consulta

            queen = ants.filter(type_ant='Q')
            if queen.exists(): # Attack Conquest
                self.take_anthill(ants, queen)
                info = _(u'El hormiguero ' + self.base_finish.get_coordinates_string() +
                            u' ha sido conquistado.')
                self.status = 'C'

            else: # No Conquest
                capacidad = self.total_transport_capacity(ants)
                self.stolen_resources = round(min([capacidad, self.base_finish.resources]))

                self.base_finish.player = None
                self.base_finish.resources = 0
                self.base_finish.save()
                info = _(u'El hormiguero ' + self.base_finish.get_coordinates_string() +
                            u' ha sido destruído. No hay hormiga princesa. No hay conquista. ' +
                            unicode(self.stolen_resources) + u' recursos son robados.')

                self.status = 'B'


            # Anular todos los ataques, apoyos y movimientos lanzados desde la base_finish destruída o conquistada.

            self.base_finish.ants_base.all().delete()
            
            # attacks = Attack.objects.filter(base_start=self.base_finish, status='P').filter(status='B').update(status='N')
            Attack.objects.filter(base_start=self.base_finish).filter(Q(status='P') | Q(status='F') | Q(status='B')).update(status='N')
            """
            PROBANDO UPDATE EN QUERYSETS
            for attack in attacks:
                attack.status = 'N'
                attack.save()
            """

            # Se cancela cualquier apoyo que partiera o llegase al hormiguero
            Uphold.objects.filter(base_start=self.base_finish).filter(Q(status='P') | Q(status='F') | Q(status='B')).update(status='N')
            Uphold.objects.filter(base_finish=self.base_finish).filter(Q(status='P') | Q(status='F') | Q(status='B')).update(status='N')

            
            # Se cancela cualquier movimiento de recurso que partiera o llegase
            MovementResources.objects.filter(base_start=self.base_finish
                                            ).filter(Q(status='P')|Q(status='F')
                                            |Q(status='B')).update(status='N')
            MovementResources.objects.filter(base_finish=self.base_finish
                                            ).filter(Q(status='P')|Q(status='F')
                                            |Q(status='B')).update(status='N')


            # dar jugador por perdido si es último hormiguero
            # dar partida por ganada si es el último rival en la partida
            # if player_finish.bases.count() == 0: 
            if player_finish.is_player_over(): # Player Over - Eliminated - Dead
                player_finish.player_over()
            if game.is_game_over():
                game.game_over()

            ants_attack_finish = self.comma_separated_ants(ants)
            ants_defend_finish = '0,0,0,0,0,0'

        else: # defender win
            print "Gana defensor"

            if total_force_finish > total_force_start * 3: # Puede cambiar éste parámetro
                invisible = True

            self.base_finish.player.kill_points += ants.count()
            self.base_finish.player.save()
            ants.delete()
            p = 100 * pow(total_force_start / total_force_finish, 1.5)

            num_ants_dead = self.remove_ants(ants_finish, p)
            self.base_start.player.kill_points += num_ants_dead
            self.base_start.player.save()

            # ants_finish = self.base_finish.ants_base
            ants_finish = Ant.objects.filter(base=self.base_finish) # Intentar no tener que hacer ésta consulta

            info = _(u'Gana el defensor.')
            ants_attack_finish = '0,0,0,0,0,0'
            ants_defend_finish = self.comma_separated_ants(ants_finish)
            self.status = 'S'

        report = BattleReport(attack=self, ants_attack_start=ants_attack_start,
                    ants_attack_finish=ants_attack_finish, ants_defend_start=ants_defend_start,
                    ants_defend_finish=ants_defend_finish, name_start=name_start,
                    name_finish=name_finish, info=info, invisible=invisible)
        report.save()


    # private
    def calculate_bonus(self, total_force_start, total_force_finish):
        bonus_start, bonus_finish = 0, 0
        
        """
        # 1º Bono de 'moral' para el defensor (si el atacante está más desarrollado que el defensor).
        growth_finish = self.base_finish.player.get_growth_statistics()
        growth_start = self.base_start.player.get_growth_statistics()
        if growth_finish < growth_start:
            p = pow(growth_start / growth_finish, 0.2) - 1
            bonus_finish += total_force_finish * p
        """


        # 2º Comprobación de bonus de los jugadores
        # 3º Comprobación de hormigueros 'especiales'
        # No es acumulativo
        if self.base_start.player.bonus == 'A' or self.base_start.player.bases.filter(bonus='A').exists():
            bonus_start += total_force_start * 0.1
        if self.base_finish.player.bonus == 'D' or self.base_finish.player.bases.filter(bonus='D').exists():
            bonus_finish += total_force_finish * 0.1
        
        """
        if self.base_start.player.bases.filter(bonus='A').exists():
            bonus_start += total_force_start * 0.1
        if self.base_finish.player.bases.filter(bonus='D').exists():
            bonus_finish += total_force_finish * 0.1
        """

        # 4º Comprobación de murallas defensivas?
        # ...

        return bonus_start, bonus_finish


    # private
    def get_power_attack(self, ants):
        """
            Get attack power of ants.
        """
        count = 0.0
        #count += ants.filter(type_ant = 'W').count() * settings.ATTACK_POWER[0]
        count += ants.filter(type_ant = 'OO').count() * settings.ATTACK_POWER[1]
        count += ants.filter(type_ant = 'OD').count() * settings.ATTACK_POWER[2]
        count += ants.filter(type_ant = 'FO').count() * settings.ATTACK_POWER[3]
        count += ants.filter(type_ant = 'FD').count() * settings.ATTACK_POWER[4]
        #count += ants.filter(type_ant = 'Q').count() * settings.ATTACK_POWER[5]
        return count


    # private
    def total_transport_capacity(self, ants):
        """
            Get transport capacity of ants.
        """
        count = 0
        count += ants.filter(type_ant = 'W').count() * settings.TRANSPORT_CAPACITY[0]
        count += ants.filter(type_ant = 'OO').count() * settings.TRANSPORT_CAPACITY[1]
        count += ants.filter(type_ant = 'OD').count() * settings.TRANSPORT_CAPACITY[2]
        count += ants.filter(type_ant = 'FO').count() * settings.TRANSPORT_CAPACITY[3]
        count += ants.filter(type_ant = 'FD').count() * settings.TRANSPORT_CAPACITY[4]
        # count += ants.filter(type_ant = 'Q').count() * settings.TRANSPORT_CAPACITY[5]
        return count


    # private
    def get_power_defense(self, ants):
        """
            Get defense power of ants.
        """
        count = 0.0
        count += ants.filter(type_ant = 'W').count() * settings.DEFENSE_POWER[0]
        count += ants.filter(type_ant = 'OO').count() * settings.DEFENSE_POWER[1]
        count += ants.filter(type_ant = 'OD').count() * settings.DEFENSE_POWER[2]
        count += ants.filter(type_ant = 'FO').count() * settings.DEFENSE_POWER[3]
        count += ants.filter(type_ant = 'FD').count() * settings.DEFENSE_POWER[4]
        count += ants.filter(type_ant = 'Q').count() * settings.DEFENSE_POWER[5]
        return count


    # private
    def remove_ants(self, ants, p):
        count = 0
        count += self.remove_type_ants(ants.filter(type_ant='W'), p)
        count += self.remove_type_ants(ants.filter(type_ant='OO'), p)
        count += self.remove_type_ants(ants.filter(type_ant='OD'), p)
        count += self.remove_type_ants(ants.filter(type_ant='FO'), p)
        count += self.remove_type_ants(ants.filter(type_ant='FD'), p)
        count += self.remove_type_ants(ants.filter(type_ant='Q'), p)
        return count


    # private
    def remove_type_ants(self, ants, p):
        """
            Delete p% of ants.
        """
        num_ants = ants.count()

        if ants.count() == 0:
            count = 0
        else:
            p = p/100.0
            num_ants_eliminate = int(round(num_ants * p)) # sobra la conversión a int...

            # Nunca podemos eliminar más de las que existen
            if num_ants_eliminate > num_ants:
                ants.delete()
                count = num_ants
            else:
                count = num_ants_eliminate - 1
                while count >= 0:
                    ants[count].delete()
                    count -= 1

                """
                count = num_ants_eliminate - 1
                while count < num_ants_eliminate:  # -1
                    ants[count].delete()
                    count += 1
                """

        return count


    # private
    def take_anthill(self, ants, queen):
        """
            The attacker take the anthill. He need a princess-queen ant.
        """

        # self.base_finish.status = 'O'
        self.base_finish.player = self.base_start.player
        queen[0].delete()
        # self.base_finish.level = 1
        self.base_finish.save()

        ants.update(in_attack=None, position='C', base=self.base_finish)
        """
        for ant in ants:
            ant.in_attack = None
            ant.position = 'C'
            ant.base = self.base_finish
            ant.save()
        """

        # + 10 Workers
        new_workers_ants = []
        for i in range(10):
            new_workers_ants.append(Ant(type_ant='W', base=self.base_finish,
                    player=self.base_finish.player, position='C'))
        Ant.objects.bulk_create(new_workers_ants)

        print "HORMIGUERO CONQUISTADO"


    # private
    def calculate_seconds(self):
        ants = Ant.objects.filter(in_attack = self)
        diff_x = self.base_start.x_coordinate - self.base_finish.x_coordinate
        diff_y = self.base_start.y_coordinate - self.base_finish.y_coordinate
        hypotenuse = math.sqrt(pow(diff_x, 2) + pow(diff_y, 2))

        if ants.filter(type_ant = 'Q').exists():
            slow_speed = settings.SPEED[5]
        elif ants.filter(type_ant = 'W').exists():
            slow_speed = settings.SPEED[0]
        elif ants.filter(type_ant = 'OO').exists():
            slow_speed = settings.SPEED[1]
        elif ants.filter(type_ant = 'OD').exists():
            slow_speed = settings.SPEED[2]
        elif ants.filter(type_ant = 'FD').exists():
            slow_speed = settings.SPEED[4]
        elif ants.filter(type_ant = 'FO').exists():
            slow_speed = settings.SPEED[3]
        else:
            print "PROBLEMS" # Exception interna...
            slow_speed = settings.SPEED[0]

        """
            slow_speed -----> 60 seconds
            hypotenuse -----> X seconds
        """
        seconds = (hypotenuse * 60) / slow_speed # 60 seconds
        
        bonus_seconds = 0
        # Bonus Player or Bonus Base
        # No son acumulativos
        if self.base_start.player.bonus == 'S' or self.base_start.player.bases.filter(bonus='S').exists(): 
            bonus_seconds += seconds * 0.1

        """
        if self.base_start.player.bases.filter(bonus='S').exists(): # Bonus Base
            bonus_seconds += seconds * 0.1
        """

        seconds = round(seconds - bonus_seconds)
        return seconds


    # private
    def ants_attach_to_attack(self, ants, num_attached_ants):

        if num_attached_ants > ants.count():
            raise Exception(u'No tienes suficientes hormigas para éste ataque.')

        """
        PROBAR:
        for index, ant in enumerate(ants):
            if index == num_attached_ants:
                break
            elif index < num_attached_ants:
                ant.in_attack = self
                ant.position = 'M'
                ant.save()
        """

        index = 0
        # REVISAR UN POSIBLE UPDATE AQUÍ...
        for ant in ants:
            if index == num_attached_ants:
                break
            elif index < num_attached_ants:
                ant.in_attack = self
                ant.position = 'M'
                ant.save()
                index += 1


    #private
    def comma_separated_ants(self, ants):
        ants_attack = ''
        ants_attack += str(ants.filter(type_ant='W').count())
        ants_attack += ','
        ants_attack += str(ants.filter(type_ant='OO').count())
        ants_attack += ','
        ants_attack += str(ants.filter(type_ant='OD').count())
        ants_attack += ','
        ants_attack += str(ants.filter(type_ant='FO').count())
        ants_attack += ','
        ants_attack += str(ants.filter(type_ant='FD').count())
        ants_attack += ','
        ants_attack += str(ants.filter(type_ant='Q').count())
        return ants_attack



    """

    MEJORAR EL MANEJO DE ANTS EN MOVIMIENTOS, ATAQUES O APOYOS..

    Comprobar eficiencia de las diferentes consultas, para saber cuál es más eficiente.
    # ants = Ant.objects.filter(in_movement_resources = self)
    # ants = Ant.objects.filter(type_ant = 'W', player=self.base_start.player, in_movement_resources = self)
    self.base_start.ants_base.filter(in_movement_resources = self)
    self.base_start.ants_base.filter(player=self.base_start.player, type_ant = 'W', in_movement_resources = self)
    # ants = Ant.objects.filter(player=self.base_start.player, type_ant = 'W', in_movement_resources = self)
    """
